#include "request-handler-client.hh"

#include "../display/quadric.hh"

#include <tools/utility.hh>

#include "../content-handler.hh"
#include "request-client.hh"

RequestHandlerClient::RequestHandlerClient (tbb::concurrent_hash_map<int, boost::shared_ptr<Object> >* objmap,
					    tbb::concurrent_hash_map<int, boost::shared_ptr<Displayable> >* disp,
					    NetworkManager& nm):
  objmap_ (objmap),
  disp_ (disp),
  rts_map_ (0),
  fps_map_ (0),
  fps_map_ok_ (false),
  nm_ (nm),
  queue_ ((ActionQueue::Instance ())->queue_get ())
{
}

void
RequestHandlerClient::treat (const boost::shared_ptr<GetObject>& o, const int* clientid)
{
  (void) clientid;
  GetObjectClient* oc = dynamic_cast<GetObjectClient*> (o.get ());
  assert (oc);

  waitingobj_.erase (oc->objectid ());

  boost::shared_ptr<Object> obj (ContentHandler::get ()->mesh (oc->meshid (),
							       oc->bounding (),
							       oc->pos (),
							       oc->angle ()));

  tbb::concurrent_hash_map<int, boost::shared_ptr<Object> >::accessor acc;
  if (objmap_->find (acc, oc->objectid ()))
    acc->second->update (obj);
  else
    objmap_->insert (std::make_pair<int, boost::shared_ptr<Object> > (o->objectid (), obj));
}

void
RequestHandlerClient::treat (const boost::shared_ptr<AskMap>& o, const int* clientid)
{
  boost::shared_ptr<AskMapClient> oc = boost::dynamic_pointer_cast<AskMapClient> (o);
  assert (oc);

  (void) clientid;

  int size = oc->size ();

  Map* map = 0;

  if (rts_map_)
  {
    fps_map_ = new Map (size);
    map = fps_map_;
    build_map_fps ();
    fps_map_ok_ = true;

    //we can now get all objects
    std::string nil;
    nm_.send (RType::GET_ALL_OBJECTS, nil);
  }
  else
  {
    rts_map_ = new Map (size);
    map = rts_map_;
    build_map_rts ();

    //when done, intantiate the BOS:
    map->init_bos ();
    map->built (true);
  }
}

void
RequestHandlerClient::build_map_rts ()
{
  Vector3d* vect = new Vector3d ();

  size_t i = 0;

  int size = rts_map_->size ();

  int x = 0;
  int y = 0;
  int z = 0;
  int offset_x = 0;
  int offset_y = 0;

  bool end = false;

  Vector3d pos_2;
  unsigned char c;

  while (!end)
  {
    std::string buf = nm_.raw_read ();

    if (nm_.eh ()->error ())
    {
      std::cout << "Couldn't receive the map, exiting..." << std::endl;
      exit (1);
    }

    while (i < buf.size ())
    {
      if (buf[i] == 1 && buf[i + 1] == 1)
      {
	++x;
	if (x == 32)
	{
	  x = 0;
	  ++y;
	}
	if (y == 32)
	{
	  y = 0;
	  ++z;
	}
	if (z == 32)
	{
	  z = 0;
	  offset_x += 256;
	}
	if (offset_x == size)
	{
	  offset_x = 0;
	  offset_y += 256;
	}
	if (offset_y == size)
	{
	  end = true;
	  break;
	}
      }
      else
      {
	// on trouve la position du noeud de 2
	c = buf[i + 1];
	pos_2 (0, (c & (1 << 7)) / 64 + (c & (1 << 6)) / 64);
	pos_2 (1, (c & (1 << 5)) / 16 + (c & (1 << 4)) / 16);
	pos_2 (2, (c & (1 << 3)) / 4 + (c & (1 << 2)) / 4);

	// la position des noeud de 1
	c = buf[i];
	for (int d_x = 0; d_x < 2; ++d_x)
	  for (int d_y = 0; d_y < 2; ++d_y)
	    for (int d_z = 0; d_z < 2; ++d_z)
	      if (c & (1 << (d_z * 4 + d_y * 2 + d_x)))
	      {
		vect->val_set (offset_x + x * 8 + pos_2 (0) * 2 + d_x,
			       offset_y + y * 8 + pos_2 (1) * 2 + d_y,
			       z * 8 + pos_2 (2) * 2 + d_z);
		(*rts_map_) (vect);
	      }
      }
      i += 2;
    }

    i = 0;
  }
  delete vect;

}

void
RequestHandlerClient::build_map_fps ()
{
  Vector3d* vect = new Vector3d ();

  size_t i = 0;
  bool end = false;

  int x = 0;
  int y = 0;

  while (!end)
  {
    std::string buf = nm_.raw_read ();

    if (nm_.eh ()->error ())
    {
      std::cout << "Couldn't receive the map, exiting..." << std::endl;
      exit (1);
    }

    while (i < buf.size ())
    {
      vect->val_set (x, y, buf[i]);
      (*fps_map_) (vect);
      vect->val_set (x, y + 1, buf[i + 1]);
      (*fps_map_) (vect);

      y += 2;
      if (y == fps_map_->size ())
      {
	y = 0;
	++x;
      }
      if (x == fps_map_->size ())
      {
	end = true;
	break;
      }
      i += 2;
    }
    i = 0;
  }
}

void
RequestHandlerClient::treat (const boost::shared_ptr<Connection>& o, const int* clientid)
{
  (void) clientid;
  (void) o;
  std::cout << "ERROR: request Connection should not be received by the client" << std::endl;
  assert (false);
}

void
RequestHandlerClient::treat (const boost::shared_ptr<Disconnection>& o, const int* clientid)
{
  (void) clientid;
  (void) o;
  std::cout << "disconnecting..." << std::endl;
}

void
RequestHandlerClient::treat (const boost::shared_ptr<GetAllObjects>& o, const int* clientid)
{
  (void) clientid;
  (void) o;
  std::cout << "ERROR: request GetAllObjects should not be received by the client" << std::endl;
  assert (false);
}

void
RequestHandlerClient::treat (const boost::shared_ptr<Text>& o, const int* clientid)
{
  (void) clientid;
  queue_->push (new ActionNewMessage (o->text ()));
}

void
RequestHandlerClient::treat (const boost::shared_ptr<UpdateObj>& o, const int* clientid)
{
  (void) clientid;
  tbb::concurrent_hash_map<int, boost::shared_ptr<Object> >::accessor acc;

  if (objmap_->find (acc, o->objid ()))
    acc->second->update (o->newpos (), o->rot ());
  else if (waitingobj_.find (o->objid ()) == waitingobj_.end ())
  {
    char tmp[4];
    Utility::to_oct (o->objid (), tmp);
    std::string ttmp (tmp, 4);

    nm_.send (RType::GET_OBJECT, std::string (tmp, 4));
    waitingobj_.insert (o->objid ());
  }
}

void
RequestHandlerClient::treat (const boost::shared_ptr<UpdatePos>& o, const int* clientid)
{
  (void) clientid;
  (void) o;
  std::cout << "ERROR: request UpdatePos should not be received by the client" << std::endl;
  assert (false);
}

void
RequestHandlerClient::treat (const boost::shared_ptr<GameMode>& o, const int* clientid)
{
  (void) clientid;
  std::cout << "GameMode request received, going to mode: " << o->mode () << std::endl;
  objmap_->clear ();
  disp_->clear ();
  queue_->push (new ActionResetSelection ());
}

void
RequestHandlerClient::treat (const boost::shared_ptr<GameZone>& o, const int* clientid)
{
  (void) o;
  (void) clientid;
  Quadric* q = new Quadric (o->pos (), Vector3d (), new BoundingSphere (o->size () (0)), Quadric::Sphere);
  disp_->insert (std::make_pair ((size_t) q, boost::shared_ptr<Displayable> (q)));
  std::cout << "GameZone request received" << std::endl;
}

void
RequestHandlerClient::treat (const boost::shared_ptr<Ready>& o, const int* clientid)
{
  (void) o;
  (void) clientid;
}

void
RequestHandlerClient::treat (const boost::shared_ptr<Move>& o, const int* clientid)
{
  (void) o;
  (void) clientid;
}
